# SnowFlakes
import time,datetime
from django.conf import settings

# 64位id划分
worker_id_bits = 5
data_center_id_bits = 5
sequence_bits = 12

# 最大值取值计算
max_worker_id = -1 ^ (-1 << worker_id_bits) # 31
# -1 = 1111 1111 ------→ 1111 1111 1111 1111
# -1 << WORKER_ID_BITS = 1111 1111 1110 0000
#  -1 ^ (-1 << WORKER_ID_BITS) ----→ 0001 1111 = 31
max_data_center_id = -1 ^ (-1 << data_center_id_bits) # 31

# 移位偏移计算

work_id_shift = sequence_bits
data_center_id_shift = sequence_bits + worker_id_bits
timestamp_shift = sequence_bits + worker_id_bits + data_center_id_bits

# 序列号循环掩码
sequence_mask = -1 ^ (-1 << sequence_bits) # 4095
#   -1 = 1111 1111 1111 1111
#   -1 << SEQUENCE_BITS = 1111 0000 0000 0000
#   -1 ^ (-1 << SEQUENCE_BITS) = 0000 1111 1111 1111 = 4095

# 　配置数据库id
WORKER_ID = 1
DATA_CENTER_ID = 9
# Sat Jul  4 14:44:27 2020
IHEPOCH = 1593845067479

class SnowFlake():
    def __init__(self,sequence=0):
        # 对参数进行判断
        if WORKER_ID > max_worker_id or WORKER_ID < 0:
            raise ValueError('worker_id值越界')
        if DATA_CENTER_ID > max_data_center_id or DATA_CENTER_ID < 0:
            raise ValueError('data_center_id值越界')
        self.worker_id = WORKER_ID
        self.data_center_id = DATA_CENTER_ID
        self.sequence = sequence
        self.last_timestamp = -1  # 上次计算的时间戳

    @staticmethod
    def ctime():
        # 输出当前时间
        return time.asctime(time.localtime(time.time()))

    @staticmethod
    def days(start_time,end_time):
        a, b, c = start_time.split('-', maxsplit=2)
        q, w, e = end_time.split('-', maxsplit=2)
        time1 = datetime.datetime(int(a), int(b), int(c))
        time2 = datetime.datetime(int(q), int(w), int(e))
        days = (time2 - time1).days
        return days

    def get_timestamp(self):
        return int(time.time()*1000)

    def get_id(self):
        # 获取新id
        timestamp = self.get_timestamp()
        # 如果时钟回拨
        if timestamp < self.last_timestamp:
            raise ValueError('时钟回拨')

        # 如果为同一时间的新建订单，则序列号加1
        if timestamp == self.last_timestamp:
            self.sequence = (self.sequence + 1) & sequence_mask
            # 此时，如果该时间的序列号也用完了，则重置sequence=1，停顿到下一秒
            if self.sequence == 0:
                timestamp = self.til_next_millis(self.last_timestamp)
        else:
            # 如果为新的时间戳,则序列号从0开始添加
            self.sequence = 0

        # 重置时间戳
        self.last_timestamp = timestamp
        # 拼接id
        new_id = ((timestamp - IHEPOCH) << timestamp_shift) | (self.data_center_id << data_center_id_shift | (self.worker_id << work_id_shift)) | self.sequence
        return new_id

    def til_next_millis(self, last_timestamp):
        # 等到下一时间戳
        timestamp = self.get_timestamp()
        while timestamp <= last_timestamp:
            timestamp = self.get_timestamp()
        return timestamp
